<?php
/**
 * Common data container, map of values
 * 
 * @package WKernel
 * 
 */
require_once 'WKernel/Data/Abstract.php';
require_once 'WKernel/Data/Value.php';
class WK_Data_Map extends WK_Data_Abstract {
	protected $data = array();	
	
	public function __construct(&$data = null) {
		if(!empty($data)) 
			$this->data = &$data;
		else 
			$this->data = array();
	}
	
	public function getValue($pointer, $value_envelop = false) {
		$value = $this->quikSearch($pointer);		
		if(empty($value)) {
			$result = $this->search($pointer);		
			$value  = $this->castToObject($result['val'][0]);
		}
		if($value instanceof WK_Data_Value && !$value_envelop) $value = $value->v;
		
		return $value;
	}
	
	public function isExist($pointer) {
		print_r($pointer);
		print_r($this->data);
		var_dump($this->data);
		if(!isset($this->data[$pointer])) {
			$result = $this->search($pointer);
			if(!empty($result['map'][0]))
				return true;
			else
				return false;
		}
		else {
			return true;
		}
	}
	
	public function setValue($pointer, $value) {
		$result = $this->search($pointer);
		if(!empty($result['val'][0])) {
			$result['val'][0] =& $this->dataToSimpleType($value);
		}
		return $value;
	}
	
	public function delValue($pointer) {
		$result = $this->search($pointer);
		if(!empty($result['val'][0])) {
			unset($result['map'][0][$result['key'][0]]);
		}
	}
	
	public function delAll($pointer) {
		$result = $this->search($pointer, true);
		if(!empty($result['val'][0])) {
			for($i = 0; $i < count($result['map']); $i++)
				unset($result['map'][$i][$result['key'][$i]]);
		}
	}
	
	public function newValue($name, $value, $pointer = null){
		if(empty($pointer)) {
			$this->setOrAppend($this->data, $name, $value);		
		}
		else {
			$result = $this->search($pointer);
			if(!empty($result['val'][0])) {
				if(!is_array($result['val'][0])) $result['val'][0] = array();
				$this->setOrAppend($result['val'][0], $name, $value);
			}
		}
		return $value;
	}
	
	public function getAll($pointer) {
		$result = $this->search($pointer, true);
		$result = $this->castToObject($result['val']);
		return $result;
	}
	
	public function getKeys(){
		return array_keys($this->data);
	}	
	
	public function __toString(){
		return WK_Data::serialize($this, 'XML');
	}
	
	private function setOrAppend(&$old, $name, $new){
		if(!isset($old[$name]))			
			$old[$name] =& $this->dataToSimpleType($new);
		else{
			$tmp = $old[$name];
			$old[$name]   =  array();
			$old[$name][] =& $tmp;
			$old[$name][] =& $this->dataToSimpleType($new);
		}
	}
	
	private function quikSearch($key){
		if(isset($this->data[$key]))
			$value = $this->castToObject($this->data[$key]);
		else 
			$value = null;
		return $value;
	}
	
	private function search($pointer, $find_all = false) {		
		$pointer  = $this->parsePointer($pointer);
		$subtrees = array();
		$subtrees[] =& $this->data;
		
		foreach($pointer['keys'] as $i => $key) {
			if($pointer['controls'][$i] == '/')  
				$one_level_search = true;
			else 
				$one_level_search = false;
			
			$found = array();
			$map   = array();
			$keys  = array();
			while(true) {
				for($i = 0; $i < count($subtrees); $i++) {
					if(isset($subtrees[$i][$key])){
						if($this->isNumberedArray($subtrees[$i][$key]))
							for($j = 0; $j < count($subtrees[$i][$key]); $j++)
								$found[] =& $subtrees[$i][$key][$j];
						else
							$found[] =& $subtrees[$i][$key];
						$map[]  =& $subtrees[$i];
						$keys[] =  $key;
						if(!$find_all) break;
					}
				}
				if(!$one_level_search) {
					$subtrees = $this->nextLevel($subtrees); 
					if(empty($subtrees)) break;
				}
				else break;
			}
			$subtrees = array();
			for ($k = 0; $k < count($found); $k++) $subtrees[] =& $found[$k];			
		}
		$result = array('map' => &$map, 'key' => $keys, 'val' => &$found);
		return $result;
	}
	
	private function &nextLevel(&$subtrees){
		$new = array();
		for($i = 0; $i < count($subtrees); $i++) {
			$keys = array_keys($subtrees[$i]);
			for($j = 0; $j < count($keys); $j++) {
				if(is_array($subtrees[$i][$keys[$j]])) $new[] =& $subtrees[$i][$keys[$j]];
			}
		}
		return $new;
	}
	
	private function parsePointer ($pointer) {
		preg_match_all('/(\/{1,2})?([^\/\[]+)(\[(\d+)\])?/', $pointer, $matches);
		if(!empty($matches[0])) {
		    foreach ($matches[1] as $key => $value) {
		    	$matches[1][$key] = !empty($matches[1][$key]) ? $matches[1][$key] : '//';
		    }
			return array('controls' => $matches[1], 
						 'keys'		=> $matches[2],
						 'counds'   => $matches[4]);
		}
		else { 
			return array('controls' => array('//'),
						 'keys'     => array($pointer),
						 'counds'   => array());
		}
	}
}